Skip to content

9.14 存储过程操作

温馨提示推荐使用 《9.18 Sql 高级代理》代替本章节功能。Sql 高级代理 能够提供更容易且更易维护的方式。

9.14.1 关于存储过程

引用百度百科:

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的 SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升。

简单来说,存储过程就是关系型数据库中(Sqlite 除外)中编写逻辑的函数/方法,通过这种方式,可以将 sql 编译缓存起来,大大提高存储过程的执行效率。

这里不讨论存储过程的优缺点。

9.14.2.0 支持存储过程的数据库

SqlServer Sqlite Cosmos InMemoryDatabase MySql PostgreSQL Oracle Firebird Dm

9.14.2 存储过程使用

Furion 框架中提供了多种存储过程操作方式:

  • ISqlRepositorySql 操作仓储,可通过 依赖注入
  • ISqlDispatchProxySql 代理方式(高级用法,推荐)
  • 通过任意实体仓储操作:personRepository.SqlProcedureQuery(procName)
  • 通过字符串拓展方法:procName.SqlProcedureQuery()
  • 通过 repository.Sql().SqlProcedureQuery() 方式

9.14.2.1 初始化方式

  • ISqlRepository
  • ISqlDispatchProxy
  • 实体仓储方式
  • 字符串拓展方式
using Furion.DatabaseAccessor;  
using Furion.DynamicApiController;  

namespace Furion.Application  
{  
    [DynamicApiController]  
    public class SqlService  
    {  
        private ISqlRepository _sqlRepository;  
        public SqlService(ISqlRepository sqlRepository)  
        {  
            _sqlRepository = sqlRepository;  
        }  
    }  
}  

using Furion.Core;  
using Furion.DatabaseAccessor;  
using System.Collections.Generic;  

namespace Furion.Application  
{  
    public interface ISqlExecuteProxy : ISqlDispatchProxy  
    {  
        [SqlProcedure("proc_GetPersons")]  
        List<Person> GetPersons(string keyword);  
    }  
}  

using Furion.DatabaseAccessor;  
using Furion.DynamicApiController;  

namespace Furion.Application  
{  
    [DynamicApiController]  
    public class SqlService  
    {  
        private ISqlExecuteProxy _sqlExecuteProxy;  
        public SqlService(ISqlExecuteProxy sqlExecuteProxy)  
        {  
            _sqlExecuteProxy = sqlExecuteProxy;  
        }  
    }  
}  

var persons = personRepository.SqlProcedureQuery("proc_GetPersons");  

var persons = "proc_GetPersons".SqlProcedureQuery();  

9.14.2.2 返回 DataTable

// ISqlRepository 方法  
var dataTable = _sqlRepository.SqlProcedureQuery("proc_GetPersons");  

// ISqlDispatchProxy 方式  
var dataTable = _sqlExecuteProxy.GetPersons();  // 推荐方式  

// 实体仓储方式  
var dataTable = _personRepository.SqlProcedureQuery("proc_GetPersons");  

// IRepository 非泛型方式  
var dataTable = _repository.Sql().SqlProcedureQuery("proc_GetPersons");  

// 变态懒人方式,直接通过存储过程名执行  
var dataTable = "proc_GetPersons".SqlProcedureQuery();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.2.3 返回 DataSet

// ISqlRepository 方法  
var dataSet = _sqlRepository.SqlProcedureQueries("proc_GetPersons");  

// ISqlDispatchProxy 方式  
var dataSet = _sqlExecuteProxy.GetPersons();  // 推荐方式  

// 实体仓储方式  
var dataSet = _personRepository.SqlProcedureQueries("proc_GetPersons");  

// IRepository 非泛型方式  
var dataSet = _repository.Sql().SqlProcedureQueries("proc_GetPersons");  

// 变态懒人方式,直接通过存储过程名执行  
var dataSet = "proc_GetPersons".SqlProcedureQueries();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.2.4 返回 List<T>

// ISqlRepository 方法  
var list = _sqlRepository.SqlProcedureQuery<Person>("proc_GetPersons");  

// ISqlDispatchProxy 方式  
var list = _sqlExecuteProxy.GetPersons();  // 推荐方式  

// 实体仓储方式  
var list = _personRepository.SqlProcedureQuery<Person>("proc_GetPersons");  

// IRepository 非泛型方式  
var list = _repository.Sql().SqlProcedureQuery<Person>("proc_GetPersons");  

// 变态懒人方式,直接通过存储过程名执行  
var list = "proc_GetPersons".SqlProcedureQuery<Person>();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.2.5 返回 Tuple<T1,...T8>

Furion 框架大大利用了 Tuple 的特性,将返回多个结果集转成 Tuple<T1,...T8> 类型

// ISqlRepository 方法  

// 返回一个结果集  
var list1 = _sqlRepository.SqlProcedureQueries<T1>("proc_GetPersons");  

// 返回两个结果集  
var (list1, list2) = _sqlRepository.SqlProcedureQueries<T1,T2>("proc_GetPersons");  

// 返回三个结果集  
var (list1, list2, list3) = _sqlRepository.SqlProcedureQueries<T1,T2,T3>("proc_GetPersons");  

// 返回四个结果集  
var (list1, list2, list3, list4) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4>("proc_GetPersons");  

// 返回五个结果集  
var (list1, list2, list3, list4, list5) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5>("proc_GetPersons");  

// 返回六个结果集  
var (list1, list2, list3, list4, list5, list6) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6>("proc_GetPersons");  

// 返回七个结果集  
var (list1, list2, list3, list4, list5, list6, list7) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>("proc_GetPersons");  

// 返回八个结果集  
var (list1, list2, list3, list4, list5, list6, list7, list8) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>("proc_GetPersons");  

// ==================================  

// ISqlDispatchProxy 方式,推荐方式,自动处理多个结果集  

// 返回一个结果集  
var list1 = _sqlRepository.GetDatas();  

// 返回两个结果集  
var (list1, list2) = _sqlRepository.GetDatas();  

// 返回三个结果集  
var (list1, list2, list3) = _sqlRepository.GetDatas();  

// 返回四个结果集  
var (list1, list2, list3, list4) = _sqlRepository.GetDatas();  

// 返回五个结果集  
var (list1, list2, list3, list4, list5) = _sqlRepository.GetDatas();  

// 返回六个结果集  
var (list1, list2, list3, list4, list5, list6) = _sqlRepository.GetDatas();  

// 返回七个结果集  
var (list1, list2, list3, list4, list5, list6, list7) = _sqlRepository.GetDatas();  

// 返回八个结果集  
var (list1, list2, list3, list4, list5, list6, list7, list8) = _sqlRepository.GetDatas();  

// ==================================  

// 实体仓储方式  

// 返回一个结果集  
var list1 = _personRepository.SqlProcedureQueries<T1>("proc_GetPersons");  

// 返回两个结果集  
var (list1, list2) = _personRepository.SqlProcedureQueries<T1,T2>("proc_GetPersons");  

// 返回三个结果集  
var (list1, list2, list3) = _personRepository.SqlProcedureQueries<T1,T2,T3>("proc_GetPersons");  

// 返回四个结果集  
var (list1, list2, list3, list4) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4>("proc_GetPersons");  

// 返回五个结果集  
var (list1, list2, list3, list4, list5) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5>("proc_GetPersons");  

// 返回六个结果集  
var (list1, list2, list3, list4, list5, list6) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6>("proc_GetPersons");  

// 返回七个结果集  
var (list1, list2, list3, list4, list5, list6, list7) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>("proc_GetPersons");  

// 返回八个结果集  
var (list1, list2, list3, list4, list5, list6, list7, list8) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>("proc_GetPersons");  

// ==================================  

// IRepository 非泛型方式  

// 返回一个结果集  
var list1 = _repository.Sql().SqlProcedureQueries<T1>("proc_GetPersons");  

// 返回两个结果集  
var (list1, list2) = _repository.Sql().SqlProcedureQueries<T1,T2>("proc_GetPersons");  

// 返回三个结果集  
var (list1, list2, list3) = _repository.Sql().SqlProcedureQueries<T1,T2,T3>("proc_GetPersons");  

// 返回四个结果集  
var (list1, list2, list3, list4) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4>("proc_GetPersons");  

// 返回五个结果集  
var (list1, list2, list3, list4, list5) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5>("proc_GetPersons");  

// 返回六个结果集  
var (list1, list2, list3, list4, list5, list6) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5,T6>("proc_GetPersons");  

// 返回七个结果集  
var (list1, list2, list3, list4, list5, list6, list7) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>("proc_GetPersons");  

// 返回八个结果集  
var (list1, list2, list3, list4, list5, list6, list7, list8) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>("proc_GetPersons");  

// ==================================  

// 变态懒人方式,直接通过存储过程名执行  

// 返回一个结果集  
var list1 = "proc_GetPersons".SqlProcedureQueries<T1>();  

// 返回两个结果集  
var (list1, list2) = "proc_GetPersons".SqlProcedureQueries<T1,T2>();  

// 返回三个结果集  
var (list1, list2, list3) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3>();  

// 返回四个结果集  
var (list1, list2, list3, list4) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4>();  

// 返回五个结果集  
var (list1, list2, list3, list4, list5) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5>();  

// 返回六个结果集  
var (list1, list2, list3, list4, list5, list6) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5,T6>();  

// 返回七个结果集  
var (list1, list2, list3, list4, list5, list6, list7) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>();  

// 返回八个结果集  
var (list1, list2, list3, list4, list5, list6, list7, list8) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.2.6 返回 单行单列 object

// ISqlRepository 方法  
var value = _sqlRepository.SqlProcedureScalar("proc_GetName");  

// ISqlDispatchProxy 方式  
var value = _sqlExecuteProxy.GetName();  // 推荐方式  

// 实体仓储方式  
var value = _personRepository.SqlProcedureScalar("proc_GetName");  

// IRepository 非泛型方式  
var value = _repository.Sql().SqlProcedureScalar("proc_GetName");  

// 变态懒人方式,直接通过存储过程名执行  
var value = "proc_GetName".SqlProcedureScalar();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.2.7 返回 单行单列 <T>

// ISqlRepository 方法  
var value = _sqlRepository.SqlProcedureScalar<string>("proc_GetName");  

// ISqlDispatchProxy 方式  
var value = _sqlExecuteProxy.GetName();  // 推荐方式  

// 实体仓储方式  
var value = _personRepository.SqlProcedureScalar<string>("proc_GetName");  

// IRepository 非泛型方式  
var value = _repository.Sql().SqlProcedureScalar<string>("proc_GetName");  

// 变态懒人方式,直接通过存储过程名执行  
var value = "proc_GetName".SqlProcedureScalar<string>();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.2.8 执行但无返回

// ISqlRepository 方法  
_sqlRepository.SqlProcedureNonQuery("proc_UpdateData");  

// ISqlDispatchProxy 方式  
_sqlExecuteProxy.UpdateData();  // 推荐方式  

// 实体仓储方式  
_personRepository.SqlProcedureNonQuery("proc_UpdateData");  

// IRepository 非泛型方式  
_repository.Sql().SqlProcedureNonQuery("proc_UpdateData");  

// 变态懒人方式,直接通过存储过程名执行  
"proc_UpdateData".SqlProcedureNonQuery();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.3 执行复杂存储过程

在存储过程中,有一种例子非常复杂,那就是既有 INPUT 参数,又有 OUTPUT 参数,还有 RETURN 参数,同时还输出 结果集 💥,如:

CREATE PROC PROC_Output  
    @Id INT,    // 输入参数  
    @Name NVARCHAR(32) OUTPUT,  // 输出参数,还带长度  
    @Age INT OUTPUT // 输出参数  
AS  
BEGIN  
    SET @Name = 'Furion Output';  

    // 输出结果集  
    SELECT *  
    FROM dbo.Test  
    WHERE Id > @Id;  

    // 输出结果集  
    SELECT TOP 10  
           *  
    FROM dbo.Test;  

    SET @Age = 27;  

    // 带 RETURN 返回  
    RETURN 10;  
END;  

9.14.3.1 创建参数模型

using Furion.DatabaseAccessor;  
using System.Data;  

namespace Furion.Application  
{  
    public class ProcOutputModel  
    {  
        public int Id { get; set; } // 输入参数  

        [DbParameter(ParameterDirection.Output, Size = 32)]  
        public string Name { get; set; }    // 输出参数  

        [DbParameter(ParameterDirection.Output)]  
        public int Age { get; set; }    // 输出参数  

        [DbParameter(ParameterDirection.ReturnValue)]  
        public int ReturnValue { get; set; }    // 返回值  
    }  
}  

9.14.3.2 执行复杂存储过程

  • ** DataSet ** 方式
// ISqlRepository 方法  
ProcedureOutputResult result = _sqlRepository.SqlProcedureOutput("proc_Complex", new ProcOutputModel{});  

// ISqlDispatchProxy 方式  
ProcedureOutputResult result = _sqlExecuteProxy.Complex(new ProcOutputModel{});  // 推荐方式  

// 实体仓储方式  
ProcedureOutputResult result = _personRepository.SqlProcedureOutput("proc_Complex", new ProcOutputModel{});  

// IRepository 非泛型方式  
ProcedureOutputResult result = _repository.Sql().SqlProcedureOutput("proc_Complex", new ProcOutputModel{});  

// 变态懒人方式,直接通过存储过程名执行  
ProcedureOutputResult result = "proc_Complex".SqlProcedureOutput(new ProcOutputModel{});  

// 获取 OUTPUT 参数值  
var outputs = result.OutputValues;  

// 获取 RETURN 返回值  
var reval = result.ReturnValue;  

// 获取返回结果集  
var dataSet = result.Result;  

  • Tuple<T1,...T8> 方式
// ISqlRepository 方法  
ProcedureOutputResult<(List<Test>, List<Test>)> result = _sqlRepository.SqlProcedureOutput<(List<Test>, List<Test>)>("proc_Complex", new ProcOutputModel{});  

// ISqlDispatchProxy 方式  
ProcedureOutputResult<(List<Test>, List<Test>)> result = _sqlExecuteProxy.Complex(new ProcOutputModel{});  // 推荐方式  

// 实体仓储方式  
ProcedureOutputResult<(List<Test>, List<Test>)> result = _personRepository.SqlProcedureOutput<(List<Test>, List<Test>)>("proc_Complex", new ProcOutputModel{});  

// IRepository 非泛型方式  
ProcedureOutputResult<(List<Test>, List<Test>)> result = _repository.Sql().SqlProcedureOutput<(List<Test>, List<Test>)>("proc_Complex", new ProcOutputModel{});  

// 变态懒人方式,直接通过存储过程名执行  
ProcedureOutputResult<(List<Test>, List<Test>)> result = "proc_Complex".SqlProcedureOutput<(List<Test>, List<Test>)>(new ProcOutputModel{});  

// 获取 OUTPUT 参数值  
var outputs = result.OutputValues;  

// 获取 RETURN 返回值  
var reval = result.ReturnValue;  

// 获取返回结果集  
var (list1,list2) = result.Result;  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.14.3 关于 [DbParameter]

[DbParameter] 特性是用来标注 Sql函数存储过程 参数的,可配置属性:

  • Direction:设置参数方向,ParameterDirection 枚举类型,默认 ParameterDirection.Input
  • DbType:设置参数类型,DbType 枚举类型,无默认
  • Size:设置参数长度的,int 类型

其中 Direction 属性是默认构造函数参数。

9.14.4 关于 ProcedureOutputResult

ProcedureOutputResultProcedureOutputResult<TResult> 是复杂存储过程执行返回模型类,有以下属性:

  • OutputValues:多个输出值,ProcedureOutputValue 类型
  • ReturnValue:返回值,object 类型
  • Result:结果集,非泛型版本是 DataSet类型,否则是 泛型类型

9.14.5 存储过程参数

所有 sql 参数都支持四种方式:

  • DbParameter[]:数组类型
  • new {}:匿名类型
  • new Class{}:强类型类型(支持复杂存储过程参数)
  • Dictionary<string,object> 类型

9.14.6 反馈与建议

与我们交流给 Furion 提 Issue